/**
 * @author W
 * @date 2020/4/6 17:04
 */

// 索引类型

// 先看一个简单的例子
{
    interface People {
        name:string,
        age:number,
        weight:number
    }

    let people:People= {
        name:'wang.mao',
        age:22,
        weight:100
    };

    // 根据一个拥有属性集合的数组，返回每个属性对应某个对象上的属性值的集合
    function getProperties(obj:any,keys:string[]):any[]{
        return keys.map(item => obj[item])
    }

    function get2<T,K extends keyof T>(obj:T,keys:K[]):T[K][] {
        return keys.map(item => obj[item])
    }


    console.log(getProperties(people,['name','weight'])); // ['wang.mao',100]
    console.log(getProperties(people,['height','grade'])) // [undefined,undefined]
    // 传入对象上没有的属性，返回undefined，但是ts没有给出错误提示，而是允许我们传入任何属性
}

// 我们希望当传入对象上没有的属性是，给出错误提示
// 使用索引类型可以达到我们的目的
{
    interface People {
        name:string,
        age:number,
        weight:number
    }

    // 索引查询操作符：keyof objType，返回这个类型上所有属性的联合类型
    let keys:keyof People ; // 相当于联合类型中字符串字面量类型：'name' | 'age' | 'weight'
    keys = 'name';
    // keys = 'grade' // error 因为 grade不属于联合类型中的某一个


    // 索引访问操作符： T[K] 表示K属性在对象T中的属性值类型
    let valueType:People['name'] ;// People['name'] 表示是string类型
    valueType = 'wang.mao'
    // valueType = 22 //error 因为number类型不能赋给string类型
}

// 结合泛型约束就可以达到目的
{
    interface People {
        name:string,
        age:number,
        weight:number
    }

    // 这里使用了泛型约束 K extend OneTye ,表示K上存在有OneType上的属性，这里使用了索引查询操作符 keyof 返回联合类型，所以K就是一个联合类型
    function getValues<T,K extends keyof T>(obj:T,keys:K[]): T[K][]{ // keys:K[] 这里就表示传入的数组是联合类型K组成的数组，意思就是数组中的属性必须是T上的 如：('name' | 'age')[]
        return keys.map(key => obj[key])  // 返回一个数组，数组值的类型是 T[K],表示由K中所有属性在类型T中的属性值类型一起组成的联合类型的数组：如 (string | number)[]
    }

    let people:People = {
        name:'LiuHuang',
        age:22,
        weight:100
    };

    console.log(getValues<People,"name">(people, ['name'])) // ['LiuHuang',22]
    // console.log(getValues(people, ['weight','height'])) // [100,undefined] 我们发现传入不在people上的属性时，编辑器就会报错
}
